package cate.game.role.bag.furniture;

import cate.common.table.d.GDFurniture;
import cate.common.table.item.furniture.row.FurnitureItemBaseRow;
import cate.common.table.item.furniture.row.FurnitureSuiteRow;
import cate.common.util.GameResult;
import cate.game.attr.FurniturePowerAttrPart;
import cate.game.attr.PowerAttrPart;
import cate.game.client.msg.MergeMsgSender;
import cate.game.event.hero.HeroPowerChangeEvent;
import cate.game.play.support.PlayBuildContext;
import cate.game.role.Role;
import cate.game.role.RoleDependent;
import cate.game.role.bag.BagContainer;
import cate.game.role.bag.hero.Hero;
import cate.game.role.bag.hero.OnHeroItem;
import cate.game.train.furniture.msg.FurnitureOffResp;
import com.fasterxml.jackson.annotation.JsonIgnore;
import cp.solution.util.collection.MapKit;
import easy.java.dev.note.NoteClass;
import easy.java.dev.note.NoteField;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.annotation.Transient;

import java.util.*;
import java.util.stream.Collectors;

@NoteClass(value = "手办")
public class FurnituresOn extends RoleDependent implements OnHeroItem<Furniture> {
    @JsonIgnore
    @Transient
    public transient Hero owner;

    @NoteField(value = "英雄身上的手办")
    public Map<Byte, Furniture> furnitureMap;

    public FurnituresOn() {

    }

    public void initialize(Hero owner, Role role) {
        super.initialize(role);
        this.owner = owner;
        if (furnitureMap == null) {
            furnitureMap = new HashMap<>();
        }
        furnitureMap.values().forEach(a -> a.initialize(role));
    }

    private boolean changePos(String furnitureUid) {
        return furnitureMap.values().stream().anyMatch(e -> StringUtils.equals(e.uid, furnitureUid));
    }

    private byte getOldPos(String furnitureUid) {
        Map.Entry<Byte, Furniture> entry = furnitureMap.entrySet().stream().filter(e -> StringUtils.equals(e.getValue().uid, furnitureUid)).findAny().orElse(null);
        return entry == null ? -1 : entry.getKey();
    }

    public Furniture getFurniture(String furnitureUid) {
        return furnitureMap.values().stream().filter(e -> StringUtils.equals(e.uid, furnitureUid)).findAny().orElse(null);
    }

    @Override
    public GameResult<Byte> on(PlayBuildContext ctx, Hero hero, byte pos, Furniture e, MergeMsgSender sender){
        GameResult<Byte> r = new GameResult<>();
        if (e == null || e.num <= 0) {
            return r.fail("物品不存在");
        }
        GameResult<Void> checkR = preCheckOn(ctx, pos, e);
        if (!checkR.ok()){
            return r.fail(checkR);
        }
        byte oldPos = getOldPos(e.uid);
        if (oldPos >= 0) {
            role.sendNow(new FurnitureOffResp(hero.uid, Collections.singletonList(oldPos)));
        }
        doRealOn(pos,e);
        r.data = pos;
        getItemBag().removeItemByUid(e.uid,1, sender);
        if (oldPos < 0) {
            //主角战力变更
            e.getRole().getBase().updatePower();
            //英雄战力变更
            if (hero != null) {
                publishEvent(ctx, new HeroPowerChangeEvent(hero.getRole(), hero));
            }
        }
        return r;
    }

    @Override
    public GameResult<Void> preCheckOn(PlayBuildContext ctx, byte pos, Furniture furniture) {
        FurnitureItemBaseRow row = furniture.getTpl(ctx);
        GameResult<Void> r = new GameResult<>();
        if (row == null) {
            return r.fail("手办配置有误");
        }
        if (pos < 0 || pos > 34) {
            return r.fail("错误的摆放位置");
        }
        boolean changePos = changePos(furniture.uid);
        if (!changePos) {
            if (furnitureMap.containsKey(pos)) {
                return r.fail("该位置已经被占用");
            }
            if (!putLimit(row.type)) {
                return r.fail("该类型手办数量已达上限");
            }
        }
        List<Byte> occupyPos = occupyPos(ctx, furniture.tid, pos);
        if (occupyPos.isEmpty()) {
            return r.fail("错误的摆放位置");
        }
        for (Map.Entry<Byte, Furniture> fur : furnitureMap.entrySet()) {
            if (changePos && StringUtils.equals(fur.getValue().uid, furniture.uid)) {
                continue;
            }
            List<Byte> furPos = occupyPos(ctx, fur.getValue().tid, fur.getKey());
            if (!ListUtils.intersection(occupyPos, furPos).isEmpty()) {
                return r.fail("错误的摆放位置");
            }
        }
        //校验位置
        return r.success();
    }

    private boolean putLimit(int type) {
        int equipedNum = (int) furnitureMap.values().stream().filter(e -> e.getTpl(role.toPlayBuildContext()).type == type).count();
        return equipedNum < role.getGame().table.furniture.limit.getTypeLimit(type);
    }

    private List<Byte> occupyPos(PlayBuildContext ctx, int tid, byte pos) {
        List<Byte> posList = new ArrayList<>();
        FurnitureItemBaseRow baseRow = ctx.furnitureExcel().base.get(tid);
        if (baseRow != null) {
            int height = baseRow.height;
            int width = baseRow.width;
            byte x = (byte) (pos % GDFurniture.BAG_WIDTH);
            byte y = (byte) (pos / GDFurniture.BAG_WIDTH);
            for (int i = 0; i < height; ++i) {
                for (int j = 0; j < width; ++j) {
                    byte tmpX = (byte) (x + j);
                    byte tmpY = (byte) (y + i);
                    if (tmpX >= GDFurniture.BAG_WIDTH || tmpY >= GDFurniture.BAG_HEIGHT) {
                        return Collections.emptyList();
                    }
                    posList.add(calcPos(tmpX, tmpY));
                }
            }
        }
        return posList;
    }

    private byte calcPos(byte x, byte y) {
        return (byte) (y * GDFurniture.BAG_WIDTH + x);
    }

    @Override
    @JsonIgnore
    public BagContainer<Furniture> getItemBag() {
        return role.getBag().furniture;
    }

    @Override
    public Map<Byte, Furniture> getPosItems() {
        return furnitureMap;
    }

    @Override
    public void doRealOn(byte pos, Furniture furniture) {
        if (changePos(furniture.uid)) {
            furnitureMap.entrySet().removeIf(e -> StringUtils.equals(e.getValue().uid, furniture.uid));
        }
        // 穿上装备
        furnitureMap.put(pos, furniture);
        role.getHistory().action.furnitureOnTime();
    }

    @Override
    public void doRealOff(byte pos, Furniture furniture) {
        this.furnitureMap.remove(pos);
    }

    @Override
    public PowerAttrPart getPowerAttrPart(PlayBuildContext ctx) {
        return new FurniturePowerAttrPart(ctx, this);
    }

    @JsonIgnore
    public List<Integer> getSkills(PlayBuildContext ctx) {
        if (owner == null) {
            return new ArrayList<>();
        }
        List<Integer> skills = new ArrayList<>();
        Map<Integer, Integer> suites = furnitureMap.values()
                .stream()
                .filter(e -> e.getTpl(ctx).suiteId > 0)
                .collect(Collectors.toMap(e -> e.getTpl(ctx).suiteId, e -> 1, Integer::sum));
        List<FurnitureSuiteRow> rows = ctx.furnitureExcel().suite.getList();
        for (FurnitureSuiteRow row : rows) {
            if (suites.containsKey(row.suiteId) && suites.get(row.suiteId) >= row.num) {
                skills.add(row.skillId);
            }
        }
        return skills;
    }

    @Override
    public FurnituresOn copy() {
        FurnituresOn cp = new FurnituresOn();
        cp.furnitureMap = MapKit.cloneDeep(this.furnitureMap);
        cp.initialize(this.owner, this.role);
        return cp;
    }
}
